#pragma once
#include <iostream>
#include <string>
#include <unistd.h>
#include <stdarg.h>
#include <ctime>
#include <cstring>
#include <fstream>
#include <pthread.h>
#include "LockGuard.hpp"
namespace log_ns{
    enum{
        DEBUG = 1,
        INFO,
        WARNING,
        ERROR,
        FATAL
    };
    #define SCREEN 1
    #define FILE 2
    #define glogfile "./log.txt"
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    std::string LevelToString(int level){
        switch(level)
        {
            case DEBUG: return "DEBUG";
            case INFO: return "INFO";
            case WARNING: return "WARNING";
            case FATAL: return "FATAL";
            default: return "UNKOWM";
        }
    }
    std::string GetCurrnTime(){
        time_t now = time(nullptr);
        struct tm *curr = localtime(&now);
        char buffer[128];
        snprintf(buffer, sizeof buffer, "%d-%02d-%02d %02d:%02d:%02d",
                    curr->tm_year + 1900, curr->tm_mon + 1,
                    curr->tm_mday, curr->tm_hour,
                    curr->tm_min, curr->tm_sec);
        return buffer;
    }
    class logmessage{
        public:
            std::string _level;
            pid_t _id;
            std::string _filename;
            int _filenumber;
            std::string _curr_time;
            std::string _message_info;
    };
    class log{
        public:
            log(int type = 1, const std::string& filename = glogfile)
            : _type(type)
            , _filename(filename)
            {

            }
            void FlushToScreen(const logmessage& lg){
                printf("[%s][%d][%s][%d][%s] %s",
                        lg._level.c_str(), lg._id,
                        lg._filename.c_str(),
                        lg._filenumber, lg._curr_time.c_str(),
                        lg._message_info.c_str());
            }
            void FlushToFile(const logmessage& lg){
                char logtxt[1024];
                snprintf(logtxt, sizeof logtxt, "[%s][%d][%s][%d][%s] %s",
                        lg._level.c_str(), lg._id,
                        lg._filename.c_str(),
                        lg._filenumber, lg._curr_time.c_str(),
                        lg._message_info.c_str());
                std::ofstream out(_filename, std::ios::app);
                if(!out.is_open())
                    return;
                out.write(logtxt, strlen(logtxt));
                out.close();
            }
            void FlushLog(const logmessage& lg){
                LockGuard lock(&mutex);
                switch(_type)
                {
                    case SCREEN :
                        FlushToScreen(lg);break;
                    case FILE :
                        FlushToFile(lg);break;
                }
            }
            void logMessage(std::string filename, int filenumber, int level, const char* format, ...){
                logmessage lg;
                lg._level = LevelToString(level);
                lg._id = getpid();
                lg._filename = filename;
                lg._filenumber = filenumber;
                lg._curr_time = GetCurrnTime();
                va_list ap;
                va_start(ap, format);
                char buffer[1024];
                vsnprintf(buffer, sizeof buffer, format, ap);
                va_end(ap);
                lg._message_info = buffer;
                FlushLog(lg);
            }
            void Enable(int type){
                _type = type;
            }
            private:
                int _type;
                std::string _filename;
    };
    log lg;
    #define Log(level, format, ...) do{ lg.logMessage(__FILE__, __LINE__, level, format, ##__VA_ARGS__); }while(0)
    #define EnableScreen() do{ lg.Enable(SCREEN); }while(0)
    #define EnableFile() do{ lg.Enable(FILE); }while(0)
}